/****************************************************************************
*
* Copyright (c) 2023  C*Core -   All Rights Reserved
*
* THIS SOFTWARE IS DISTRIBUTED "AS IS, " AND ALL WARRANTIES ARE DISCLAIMED,
* INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* PROJECT     : CCFC2011BC
* DESCRIPTION : CCFC2011BC MCU low level drivers code
* HISTORY     : Initial version.
* @file     Mcu_lld.c
* @version  1.1
* @date     2023 - 02 - 20
* @brief    Initial version.
*
*****************************************************************************/
#include "Mcu_lld.h"
#include "lldconf.h"

/**
* @brief    ExtOsc selection.
**/
static uint8_t Mcu_ExtOsc = ExtOSC_NULL;

/**
* @brief    MCU configuration pointer.
**/
static const Mcu_Config_t *Mcu_Cfg_Ptr = NULL_PTR;


/*******************************************************************************
 * @brief      Mcu ModeInit.
 * @param[in]  ModePtr: Mcu mode configuration pointer
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_LLD_InitMode(const Mcu_ModeConfig_t *ModePtr) /*PRQA S 1505*/
{
    /************************************************************
     *          Mcu Mode Enable
     ************************************************************/
    ME.MER.R = (uint32_t)(ModePtr->Mcu_ModeEnable_Cfg);

    /************************************************************
     *          Mcu Mode configuration
     ************************************************************/
    /* DRUN Mode Configuration */
    ME.DRUN.R = (uint32_t)(ModePtr->DRUN_Mode_Cfg);

    /* RUN0 Mode Configuration */
    ME.RUN[0].R = (uint32_t)(ModePtr->RUN0_Mode_Cfg);

    /* RESET Mode Configuration:
     * (MCU_SYSCLK_FIRC|MC_FIRC_ON|MC_FXOSC_OFF|MC_FMPLL_OFF
     * |MC_CFLS_NORMAL|MC_DFLS_NORMAL|MC_MVR_ON|MC_PDO_OFF)
     * == (ME.RESET.R) */

    /* SAFE Mode Configuration:
     * (MCU_SYSCLK_FIRC|MC_FIRC_ON|MC_FXOSC_OFF|MC_FMPLL_OFF
     * |MC_CFLS_NORMAL|MC_DFLS_NORMAL|MC_MVR_ON|MC_PDO_ON)
     * == (ME.SAFE.R) */

    /* TEST Mode Configuration */
    if (ME_ME_TEST == ((uint32_t)(ModePtr->Mcu_ModeEnable_Cfg) & ME_ME_TEST))
    {
        ME.TEST.R = (uint32_t)(ModePtr->TEST_Mode_Cfg);
    }
    /* RUN1 Mode Configuration */
    if (ME_ME_RUN1 == ((uint32_t)(ModePtr->Mcu_ModeEnable_Cfg) & ME_ME_RUN1))
    {
        ME.RUN[1].R = (uint32_t)(ModePtr->RUN1_Mode_Cfg);
    }
    /* RUN2 Mode Configuration */
    if (ME_ME_RUN2 == ((uint32_t)(ModePtr->Mcu_ModeEnable_Cfg) & ME_ME_RUN2))
    {
        ME.RUN[2].R = (uint32_t)(ModePtr->RUN2_Mode_Cfg);
    }
    /* RUN3 Mode Configuration */
    if (ME_ME_RUN3 == ((uint32_t)(ModePtr->Mcu_ModeEnable_Cfg) & ME_ME_RUN3))
    {
        ME.RUN[3].R = (uint32_t)(ModePtr->RUN3_Mode_Cfg);
    }
    /* HALT Mode Configuration */
    if (ME_ME_HALT == ((uint32_t)(ModePtr->Mcu_ModeEnable_Cfg) & ME_ME_HALT))
    {
        ME.HALT.R = (uint32_t)(ModePtr->HALT_Mode_Cfg);
    }
    /* STOP Mode Configuration */
    if (ME_ME_STOP == ((uint32_t)(ModePtr->Mcu_ModeEnable_Cfg) & ME_ME_STOP))
    {
        ME.STOP.R = (uint32_t)(ModePtr->STOP_Mode_Cfg);
    }
    /* STANDBY Mode Configuration */
    if (ME_ME_STANDBY == ((uint32_t)(ModePtr->Mcu_ModeEnable_Cfg) & ME_ME_STANDBY))
    {
        ME.STANDBY.R = (uint32_t)(ModePtr->STANDBY_Mode_Cfg);
    }

    /************************************************************
     *          Mcu Run Peripheral Configuration
     ************************************************************/
    ME.RUNPC[0].R = (uint32_t)(ModePtr->RUN_PeriphCfg0_Cfg);
    ME.RUNPC[1].R = (uint32_t)(ModePtr->RUN_PeriphCfg1_Cfg);
    ME.RUNPC[2].R = (uint32_t)(ModePtr->RUN_PeriphCfg2_Cfg);
    ME.RUNPC[3].R = (uint32_t)(ModePtr->RUN_PeriphCfg3_Cfg);
    ME.RUNPC[4].R = (uint32_t)(ModePtr->RUN_PeriphCfg4_Cfg);
    ME.RUNPC[5].R = (uint32_t)(ModePtr->RUN_PeriphCfg5_Cfg);
    ME.RUNPC[6].R = (uint32_t)(ModePtr->RUN_PeriphCfg6_Cfg);
    ME.RUNPC[7].R = (uint32_t)(ModePtr->RUN_PeriphCfg7_Cfg);

    /************************************************************
     *          Mcu Low - Power Peripheral Configuration
     ************************************************************/
    ME.LPPC[0].R = (uint32_t)(ModePtr->LowPower_PeriphCfg0_Cfg);
    ME.LPPC[1].R = (uint32_t)(ModePtr->LowPower_PeriphCfg1_Cfg);
    ME.LPPC[2].R = (uint32_t)(ModePtr->LowPower_PeriphCfg2_Cfg);
    ME.LPPC[3].R = (uint32_t)(ModePtr->LowPower_PeriphCfg3_Cfg);
    ME.LPPC[4].R = (uint32_t)(ModePtr->LowPower_PeriphCfg4_Cfg);
    ME.LPPC[5].R = (uint32_t)(ModePtr->LowPower_PeriphCfg5_Cfg);
    ME.LPPC[6].R = (uint32_t)(ModePtr->LowPower_PeriphCfg6_Cfg);
    ME.LPPC[7].R = (uint32_t)(ModePtr->LowPower_PeriphCfg7_Cfg);

    /************************************************************
     *          Mcu Mode Interrupt
     ************************************************************/
    ME.IM.R = (uint32_t)(ModePtr->InterruptMask_Cfg);
}

/*******************************************************************************
 * @brief      Mcu_Apply_Mode: Apply target device mode.
 * @param[in]  Mcu_Mode: Mcu target mode selection(ME_MODE_t)
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_Apply_Mode(uint32_t Mcu_Mode) /*PRQA S 1505*/
{
    /* Apply the configuration */
    ME.MCTL.R = ((Mcu_Mode & ME_MODE_MASK) | ME_CONTROL_KEY);
    ME.MCTL.R = ((Mcu_Mode & ME_MODE_MASK) | ME_CONTROL_INVERTED_KEY);

    /* Wait for mode transition to complete */
    while (ME_GS_MTRANS == ((ME.GS.R) & ME_GS_MTRANS))
    {
        ;
    }
}

/*******************************************************************************
 * @brief      Mcu_LLD_InitClock: Mcu ClockInit.
 * @param[in]  ClockPtr: Mcu clock configuration pointer
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_LLD_InitClock(const Mcu_ClockConfig_t *ClockPtr) /*PRQA S 1505*/
{
    /* Mcu OSC init */
    CGM.FXOSC_CTL.R = (uint32_t)(ClockPtr->FXOSC_Cfg);
    CGM.SXOSC_CTL.R = (uint32_t)(ClockPtr->SXOSC_Cfg);
    CGM.FIRC_CTL.R  = (uint32_t)(ClockPtr->FIRC_Cfg);
    CGM.SIRC_CTL.R  = (uint32_t)(ClockPtr->SIRC_Cfg);

    /* Mcu FMPLL init */
    Mcu_PLL_Init(ClockPtr);

    /* Mcu clock output init */
    Mcu_ClkOut_Init(ClockPtr);
}

/*******************************************************************************
 * @brief      Mcu ExtOsc Measure
 * @param[in]  None
 * @param[out] Mcu_ExtOsc(ExtOSC_t)
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_MeasureExtOsc_Init(void) /*PRQA S 1505*/
{
#if (MCU_MEASURE_EXOSC_SWITCH == STD_ON)
    uint32_t uMeasureExtOscVal;

    /* Wait for the frequency measurement to be completed */
    while (CGM.CMU_CSR.B.SFM == 1U)
    {
        ;
    }

    CGM.CMU_MDR.R = 256U;       /* Measurement duration bits */
    CGM.CMU_CSR.B.SFM = 1U;     /* Start frequency measure */
    while (CGM.CMU_CSR.B.SFM == 1U)
    {
        ;
    } /* Waiting for completed */

    uMeasureExtOscVal = CGM.CMU_FDR.R;

    /* fx = (fFXOSC * MD) / FDR,
     * x = FIRC, SIRC or SXOSC. */
    if (uMeasureExtOscVal > 384U)
    {
        Mcu_ExtOsc = ExtOSC_40M;
    }
    else if (uMeasureExtOscVal < 150U)
    {
        Mcu_ExtOsc = ExtOSC_8M;
    }
    else
    {
        Mcu_ExtOsc = ExtOSC_16M;
    }
#else
    Mcu_ExtOsc = MCU_EXTOSC_INIT_VAL;
#endif
}

/*******************************************************************************
 * @brief      Mcu_DRUN_Init: Apply target device mode to DRUN mode.
 *             Open CMU measure function.
 * @param[in]  None
 * @param[out] Mcu_ExtOsc(ExtOSC_t)
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_DRUN_Init(void) /*PRQA S 1505*/
{
    CGM.CMU_CSR.R = (CMU_RCDIV_8|CMU_CKSEL1_FIRC|CMU_CLK_MONITOR_DIS);

    CGM.CMU_LFREFR.R = (0xFFEU & CMU_LFREFR_MASK);

    Mcu_Apply_Mode(ME_MODE_DRUN);
}


/*******************************************************************************
 * @brief      Mcu_PLL_Init : PLL ClockInit.
 * @param[in]  ClockPtr: Mcu clock configuration pointer
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_PLL_Init(const Mcu_ClockConfig_t *ClockPtr) /*PRQA S 1505*/
{
    /* Switch to DRUN mode first. */
    Mcu_DRUN_Init();

    Mcu_MeasureExtOsc_Init();

    /* PLL function initializes,
     * Set IDF/NDIV/ODF. */
    Mcu_PLL_LLD_Init(Mcu_ExtOsc, ((uint8_t)(ClockPtr->PLLClkVal_Cfg) & PLL_VAL_MASK));

    /* Hardware to reduce instantaneous power consumption function mode,
     * don't open without need */
    if (PLL_CLK_SW_MODE == (uint8_t)((ClockPtr->PLLFuncMode_Cfg) & PLL_CLK_SW_MODE))
    {
        CGM.FMPLL_CR.R |= PLL_EN_PLL_SW;
    }

    /* Normal mode with frequency modulation */
    if (PLL_NORMAL_MODE_FM == (uint8_t)((ClockPtr->PLLFuncMode_Cfg) & PLL_NORMAL_MODE_FM))
    {
        CGM.FMPLL_MR.R = (uint32_t)(ClockPtr->FMPLL_Cfg);
    }

    Mcu_Periph_Init(ClockPtr);

    Mcu_Apply_Mode((uint32_t)(ClockPtr->TargetMode_Cfg));
}

/*******************************************************************************
 * @brief      Mcu_PLL_LLD_Init : PLL Clock LLD_Init.
 *              Set @IDF & @NDIV & @ODF.
 * @param[in]  mExtOSC: ExtOSC_t
 *             PllClkSel: PLLClkVal_t
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_PLL_LLD_Init(uint8_t mExtOSC, uint8_t PllClkSel) /*PRQA S 1505*/
{
    /*************************
     *       clkin x NDIV
     * phi = --------------
     *        IDF x ODF
     *************************/
    switch (mExtOSC)
    {
        case ExtOSC_8M:
            switch (PllClkSel)
            {
                case PLL_32M:
                    /* 0x02200000 : (8MHz*32)/(1*8) = 32MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_1;
                    CGM.FMPLL_CR.B.NDIV = NDIV_32;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_8_DEFAULT;
                    break;
                case PLL_64M:
                    /* 0x01200000    : (8MHz*32)/(1*4) = 64MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_1;
                    CGM.FMPLL_CR.B.NDIV = NDIV_32;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_4;
                    break;
                case PLL_80M:
                    /* 0x01280000    : (8MHz*40)/(1*4) = 80MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_1;
                    CGM.FMPLL_CR.B.NDIV = NDIV_40;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_4;
                    break;
                case PLL_120M:
                    /* 0x013C0000   : (8MHz*60)/(1*4) = 120MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_1;
                    CGM.FMPLL_CR.B.NDIV = NDIV_60;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_4;
                    break;
                default:
                    break;
            }
            break;
        case ExtOSC_16M:
            switch (PllClkSel)
            {
                case PLL_32M:
                    /* 0x06200000: (16MHz*32)/(2*8) = 32MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_2;
                    CGM.FMPLL_CR.B.NDIV = NDIV_32;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_8_DEFAULT;
                    break;
                case PLL_64M:
                    /* 0x06400000: (16MHz*64)/(2*8) = 64MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_2;
                    CGM.FMPLL_CR.B.NDIV = NDIV_64;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_8_DEFAULT;
                    break;
                case PLL_80M:
                    /* 0x05280000: (16MHz*40)/(2*4) = 80MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_2;
                    CGM.FMPLL_CR.B.NDIV = NDIV_40;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_4;
                    break;
                case PLL_120M:
                    /* 0x053C0000: (16MHz*60)/(2*4) = 120MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_2;
                    CGM.FMPLL_CR.B.NDIV = NDIV_60;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_4;
                    break;
                default:
                    break;
            }
            break;
        case ExtOSC_40M:
            switch (PllClkSel)
            {
                case PLL_32M:
                    /* 0x12200000 : (40MHz*32)/(5*8) = 32MHz, */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_5;
                    CGM.FMPLL_CR.B.NDIV = NDIV_32;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_8_DEFAULT;
                    break;
                case PLL_64M:
                    /* 0x12400000 : (40MHz*64)/(5*8) = 64MHz, */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_5;
                    CGM.FMPLL_CR.B.NDIV = NDIV_64;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_8_DEFAULT;
                    break;
                case PLL_80M:
                    /* 0X1d400000   : (40MHz*64)/(8*4) = 80MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_8;
                    CGM.FMPLL_CR.B.NDIV = NDIV_64;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_4;
                    break;
                case PLL_120M:
                    /* 0x0D300000   : (40MHz*48)/(4*4) = 120MHz */
                    CGM.FMPLL_CR.B.IDF  = IDF_DIV_4;
                    CGM.FMPLL_CR.B.NDIV = NDIV_48;
                    CGM.FMPLL_CR.B.ODF  = ODF_DIV_4;
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }
}

/*******************************************************************************
 * @brief      Mcu_Periph_Init :  Peripheral clock initializes.
 * @param[in]  ClockPtr: Mcu clock configuration pointer
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_Periph_Init(const Mcu_ClockConfig_t *ClockPtr) /*PRQA S 1505*/
{
    /* I2C_0~3, LINFlex_0~13 */
    CGM.SC_DC0.R = (SYSCLK_DIV_EN | ((uint8_t)(ClockPtr->PeriphDiv0_Cfg) & SYSCLK_DIV_MASK));
    /* DSPI_0~8, FlecCAN_0~7 */
    CGM.SC_DC1.R = (SYSCLK_DIV_EN | ((uint8_t)(ClockPtr->PeriphDiv1_Cfg) & SYSCLK_DIV_MASK));
    /* ADC_0~1, CTU, ETPU, eMIOS_0~1, RTC/API, PIT, CMU */
    CGM.SC_DC2.R = (SYSCLK_DIV_EN | ((uint8_t)(ClockPtr->PeriphDiv2_Cfg) & SYSCLK_DIV_MASK));
}

/*******************************************************************************
 * @brief      Mcu_ClkOut_Init : Clock output initializes.
 * @param[in]  ClockPtr: Mcu clock configuration pointer
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_ClkOut_Init(const Mcu_ClockConfig_t *ClockPtr) /*PRQA S 1505*/
{
    if (CLKOUT_EN == ((uint32_t)(ClockPtr->ClkOut_Cfg) & CLKOUT_EN))
    {
        SIU.PCR[0].R = (uint16_t)0x0A00u;

        /* Output Clock Division Select,
         * Output Clock Source Selection Control */
        CGM.OCDS_SC.R = ((uint32_t)(ClockPtr->ClkOut_Cfg) & \
                        (CLKOUT_DIV_MASK | CLKOUT_SC_MASK));
    }

    /* Output Clock Enable/Disable control */
    CGM.OC_EN.R = ((uint32_t)(ClockPtr->ClkOut_Cfg) & CLKOUT_EN);
}

/*******************************************************************************
 * @brief      Mcu initialization.
 * @param[in]  ConfigPtr: Mcu configuration pointer
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_Init(const Mcu_Config_t *ConfigPtr)
{
    if (ConfigPtr == NULL_PTR)
    {
        Mcu_Cfg_Ptr = &McuConfig_PC;
    }
    else
    {
        /* Copy pointer to config structure */
        Mcu_Cfg_Ptr = ConfigPtr; /* PRQA S 2919 */
    }

    Mcu_LLD_InitMode(Mcu_Cfg_Ptr->Mcu_ModeConfig);

    Mcu_LLD_InitClock(Mcu_Cfg_Ptr->Mcu_ClockConfig);
}

/*******************************************************************************
 * @brief      Mcu De - initialization.
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Mcu_DeInit(void)
{
    Mcu_Cfg_Ptr = NULL_PTR;
}
